using System;
using System.Collections;
using System.Collections.Generic;
using ValidationFramework.Extensions;
using ValidationFramework.Configuration.Reflection;
using ValidationFramework.Reflection;

namespace ValidationFramework.Specifications
{
	/// <summary>
	/// Specifies that a <see cref="LengthCollectionRule"/> should be applied to the program element.
	/// </summary>
	/// <seealso cref="LengthCollectionRule"/>
	/// <remarks>If <see cref="ExcludeDuplicatesFromCount"/> is true then <see cref="object.GetHashCode"/> is used to discard duplicates from the count. If the collection is null <see langword="true"/> will be returned. To validate for nulls use a <see cref="RequiredObjectRuleAttribute"/>.</remarks>
#if (!SILVERLIGHT)
	[Serializable]
#endif
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
	public sealed class HasCountInRangeAttribute : RuleAttribute
	{
		// --- Fields

        private int minimum;


		// --- Constructor


		/// <param name="maximum">The maximum length allowed.</param>
		public HasCountInRangeAttribute(int maximum)
		{
			Maximum = maximum;
		}


		// --- Properties

		/// <summary>
		/// Gets or sets whether to exclude duplicates when calculating the length.
		/// </summary>
		/// <remarks>Setting this to <see langword="true"/> will decrease the performance of <see cref="Rule.Validate"/></remarks>
		/// <seealso cref="LengthCollectionRule.ExcludeDuplicatesFromCount"/>
		public bool ExcludeDuplicatesFromCount
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets the type name for the type to get <see cref="IEqualityComparer{T}"/> from.
		/// </summary>
		/// <remarks>Both <see cref="EqualityComparerPropertyName"/> and <see cref="EqualityComparerTypeName"/> have to be null or not null.</remarks>
		/// <seealso cref="LengthCollectionRule.Comparer"/>
		public string EqualityComparerPropertyName
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets the name of the static property to get <see cref="IEqualityComparer{T}"/> from. 
		/// </summary>
		/// <remarks>Both <see cref="EqualityComparerPropertyName"/> and <see cref="EqualityComparerTypeName"/> have to be null or not null.</remarks>
		/// <seealso cref="LengthCollectionRule.Comparer"/>
		public string EqualityComparerTypeName
		{
			get;
			set;
		}

		/// <summary>
        /// Gets the maximum length.
        /// </summary>
        /// <seealso cref="LengthRule.Maximum"/>
        public int Maximum
        {
        	get;
        	private set;
        }

        /// <summary>
        /// Gets the minimum length.
        /// </summary>
        /// <exception cref="ArgumentException">Value is less than 0.</exception>
        /// <seealso cref="LengthRule.Minimum"/>
        public int Minimum
        {
            get
            {
                return minimum;
            }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentException("Minimum too low.", "value");
                }
                minimum = value;
            }
        }


		// --- Methods

		/// <inheritdoc/>
		public override ISpecification CreateSpecification(ICachedReflectionInfo descriptor)
		{
			var equalityComparer = (IEqualityComparer)TypeExtensions.GetStaticProperty(EqualityComparerTypeName, EqualityComparerPropertyName);

			return new HasCountInRangeSpec(Minimum, Maximum)
			{
				CountDuplicates = ExcludeDuplicatesFromCount,
				Comparer = equalityComparer
			};
		}
	}
}